#include <dspSimulator/instruction/pack2.h>

bool PACK2::checkCondition(std::shared_ptr<DspMachine> dspMachine) {
    return checkStandardCondition(dspMachine);
}

void PACK2::loadInstIRIntoJit(llvm::LLVMContext *llvmContext, llvm::Module *module,
                              std::shared_ptr<llvm::IRBuilder<>> irBuilder) {
    llvm::FunctionType *funcType = llvm::FunctionType::get(irBuilder->getVoidTy(), false);
    // ---------------------------------PACK2 (.unit) i2, xi2, i2 ---------------------------------------------
    {
        llvm::Function *pack2 = llvm::Function::Create(funcType, llvm::GlobalValue::ExternalLinkage, "PACK2Kind1",
                                                       module);
        llvm::BasicBlock *entryBB = llvm::BasicBlock::Create(*llvmContext, "entry", pack2);
        irBuilder->SetInsertPoint(entryBB);

        auto dstRegPtr = getDstLoRegPtr(module, irBuilder);

        auto src2RegPtr = getSrc2LoRegPtr(module, irBuilder);
        auto src2Reg = irBuilder->CreateLoad(src2RegPtr);
        auto src2LSB = irBuilder->CreateAnd(src2Reg, irBuilder->getInt32(0x0000ffff));

        auto src1RegPtr = getSrc1LoRegPtr(module, irBuilder);
        auto src1Reg = irBuilder->CreateLoad(src1RegPtr);
        auto src1LSB = irBuilder->CreateAnd(src1Reg, irBuilder->getInt32(0x0000ffff));

        auto dstMSB = irBuilder->CreateShl(src1LSB, irBuilder->getInt32(16));

        auto res = irBuilder->CreateOr(dstMSB, src2LSB);
        irBuilder->CreateStore(res, dstRegPtr);
        irBuilder->CreateRetVoid();
    }
}

std::shared_ptr<Instruction> PACK2::decode(std::shared_ptr<InstInfo> instInfo, u64 baseCycle) {
    std::shared_ptr<PACK2> pack2;
    auto dspInstInfo = std::dynamic_pointer_cast<DspInstInfo>(instInfo);
    bool isStandardInst = dspInstInfo->isStandardInstruction();
    if (isStandardInst) {
        auto data = vectorToBitSet<32>(dspInstInfo->getBits());
        if (checkBits<10>(data, 2, 0b0000000110) && existStandardCondition(data)) {
            pack2 = std::make_shared<PACK2>(dspInstInfo->getAddress(), baseCycle);
            pack2->creg_z = extractBits<4>(data, 28);
            pack2->dst = extractBits<5>(data, 23).to_ulong();
            pack2->src2 = extractBits<5>(data, 18).to_ulong();
            pack2->src1 = extractBits<5>(data, 13).to_ulong();
            pack2->x = data[12];
            pack2->s = data[1];
            pack2->p = data[0];
            if (pack2->s == 0) {
                pack2->funcUnit = FuncUnit::L1;
            } else {
                pack2->funcUnit = FuncUnit::L2;
            }
            pack2->instFormat = InstFormat::Kind1;
        } else if (checkBits<10>(data, 2, 0b1111111100) && existStandardCondition(data)) {
            pack2 = std::make_shared<PACK2>(dspInstInfo->getAddress(), baseCycle);
            pack2->creg_z = extractBits<4>(data, 28);
            pack2->dst = extractBits<5>(data, 23).to_ulong();
            pack2->src2 = extractBits<5>(data, 18).to_ulong();
            pack2->src1 = extractBits<5>(data, 13).to_ulong();
            pack2->x = data[12];
            pack2->s = data[1];
            pack2->p = data[0];
            if (pack2->s == 0) {
                pack2->funcUnit = FuncUnit::S1;
            } else {
                pack2->funcUnit = FuncUnit::S2;
            }
            pack2->instFormat = InstFormat::Kind1;
        }
    }
    return pack2;
}

void PACK2::executeCustom(std::shared_ptr<TargetMachine> targetMachine, ExecutePhase executePhase) {

}

std::shared_ptr<DspInstruction> PACK2::clone() {
    return std::make_shared<PACK2>(*this);
}

std::string PACK2::toString() const {
    std::string res;
    res += getStandardConditionString() + " " + m_name + " " + FuncUnitName[static_cast<u32>(funcUnit)] + " ";
    if (instFormat == InstFormat::Kind1) {
        res += getSrc1Reg32Name() + "," + getSrc2Reg32Name() + "," + getDstReg32Name();
    }
    return res;
}